(import
 (except (rnrs base) let-values map error)
 (only (guile)
       lambda* λ
       string-split
       ;; string-count
       ;; string-every
       string-append
       string-join
       string-trim
       string-trim-right
       ;; list->string
       )
 (fileio)
 (srfi srfi-1)
 ;; hash tables
 (srfi srfi-69)
 ;; functional record types
 (srfi srfi-9 gnu)
 ;; (srfi srfi-8)
 ;; (srfi srfi-11)
 (ice-9 pretty-print)
 ;; (ice-9 match)
 )


(define string-indent
  (λ (str indent)
    (string-join (map (λ (line) (string-append indent line))
                      (string-split str #\newline))
                 "\n")))


;; (define-immutable-record-type <fs-entry>
;;   (construct-fs-entry name size children)
;;   fs-entry?
;;   (name fs-entry-name set-fs-entry-name)
;;   (size fs-entry-size set-fs-entry-size)
;;   (children fs-entry-children set-fs-entry-children))


;; (set-record-type-printer!
;;  <fs-entry>
;;  (lambda (record port)
;;    (display "<fs-entry:\n" port)
;;    (display (string-append "    name: " (fs-entry-name record) "\n") port)
;;    (display (string-append "    size: " (number->string (fs-entry-size record)) "\n") port)
;;    (display (string-append "    children: ") port)
;;    (display
;;     (string-trim
;;      (string-indent
;;       (string-trim-right
;;        (call-with-output-string
;;          (λ (port)
;;            (pretty-print (fs-entry-children record)
;;                          port
;;                          #:width 80
;;                          #:display? #t
;;                          #:per-line-prefix ""))))
;;       "    "))
;;     port)
;;    (display "\n>" port)))


;; (define make-fs-entry
;;   (lambda* (name #:optional (size -1) (children '()))
;;     (construct-fs-entry name size children)))

(define cd-in?
  (λ (str)
    (and (string-prefix? "$ cd" str)
         (not (cd-out? str)))))

(define cd-in-dir-name
  (λ (str)
    (string-join (drop (string-split str #\space) 2) " ")))

(define cd-out?
  (λ (str)
    (string=? "$ cd .." str)))

(define ls?
  (λ (str)
    (string=? "$ ls" str)))


(define dir?
  (λ (str)
    (string-prefix? "dir" str)))


(define file?
  (λ (str)
    (let ([parts (string-split str #\space)])
      (and (= (length parts) 2)
           (number? (string->number (first parts)))))))


(define file-size
  (λ (str)
    (string->number (first (string-split str #\space)))))


(define lines (get-lines-from-file "input"))

(define limit 100000)

(define lines-filtered
  (filter (λ (line)
            (or (cd-out? line)
                (cd-in? line)
                (file? line)))
          lines))

(define dir-size-map (make-hash-table equal?))

(let iter ([lines° lines-filtered] [dir-parts° '()])
  (cond
   [(null? lines°) dir-size-map]
   [else
    (let ([line (first lines°)])
      (cond
       [(cd-in? line)
        ;; Set size of new directory initially to zero.
        (hash-table-set! dir-size-map
                         (cons (cd-in-dir-name line)
                               dir-parts°)
                         0)
        ;; Step in by adding directory name to the directory
        ;; parts.
        (iter (drop lines° 1)
              (cons (cd-in-dir-name line)
                    dir-parts°))]
       ;; Stepping out of a directory merely changes the
       ;; directory parts, dropping 1 part.
       [(cd-out? line)
        (iter (drop lines° 1)
              (drop dir-parts° 1))]
       ;; If we see a file, add it to the current
       ;; directory's size and all parents' sizes.
       [(file? line)
        (let ([size (file-size line)])
          (let update-dir-sizes ([dir-parts dir-parts°])
            (unless (null? dir-parts)
              (display
               (simple-format
                #f "updating size of ~a with ~a\n"
                dir-parts° size))
              (hash-table-update! dir-size-map
                                  dir-parts
                                  (λ (old-val) (+ old-val size))
                                  (λ () size))
              (update-dir-sizes (drop dir-parts 1)))))
        (iter (drop lines° 1) dir-parts°)]
       ;; Skip other lines.
       [else
        (iter (drop lines° 1)
              dir-parts°)]))]))

(define dirs
  (map (λ (alist-entry)
         (cons (string-join (reverse (car alist-entry)) "/")
               (cdr alist-entry)))
       (hash-table->alist dir-size-map)))

(pretty-print dirs)

(display
 (simple-format
  #f "~a\n"
  (hash-table-fold dir-size-map
                   (λ (key value previous)
                     (cond
                      [(<= value limit)
                       (display (simple-format #f "is below limit: ~a: ~a\n" key value))
                       (+ previous value)]
                      [else
                       previous]))
                   0)))

;; 695618 is too low
;; 1324872
